/*
PARI/GP scripts defining generic PARI/GP Utilities
=============================================================
File: EbUtils.txt
Link: https://oeis.org/wiki/File:EbUtils.txt
=============================================================
Last updated 19 Nov 2016.
Dependencies: none

This file defines two global variables Eps_ and MaxIter_,
used in a number of the author's PARI/GP scripts.
> Eps_ is used primarily to test near-equality of REAL values
> MaxIter_ is used  to stop runaway iterations 
It also sets the current realprecision to 50 digits, and
and initializes Eps_ and MaxIter_ accordingly.

In addition, the file contains these utility functions:
> SetEbDefaults(dgs=-1,eps=0.0,imx=-1)
> Floor(z)
> Round(z)
> IsInt(z)
> IsIntLt0(z)
> IsIntLe0(z)
> IsIntGt0(z)
> IsIntGe0(z)
> IsEq(z1,z2)
*/

\\ Definitions of Eb globals (reserved names)
\\ ==========================================================
default(realprecision,50);
Eps_ = 4.0*10.0^(-default(realprecision));
MaxIter_ = 0;  \\ Used to stop runaway iterations   

\\ ==========================================================
\\ A function to set the Eb globals
\\ ==========================================================

SetEbDefaults(dgs=-1,eps=0.0,imx=-1) = {
/* ----------------------------------------------------------
Sets realprecision (dgs digits), Eps_ (argument eps), and
MaxIter_ (argument imx). For each of these arguments,
a negative value means "do nothing; keep current value",
zero means "reset to defaults", and a positive value means
"set as indicated". The defaults are: 50 digits,
 Eps_ = 5.0*10.0^(-default(realprecision))
 MaxIter_ = 10000 iterations.
Typical calls look like this:
- Adjust (synchronize) Eps_ with current realprecision:
  SetEbDefaults() 
- Set realprecision to 1000 digits and adjust Eps_:
  SetEbDefaults(1000) 
- Set Eps_ to 3e_12:
  SetEbDefaults(,3e_12) 
- Set 100 digits, MaxIter_ to 30, and adjust Eps_:
  SetEbDefaults(100,3e_12,30) 
---------------------------------------------------------- */
  if (dgs==0,default(realprecision,50),
             if (dgs>0,default(realprecision,dgs)));
  \\ The factor 5.0 on the next line is a concession to
  \\ rounding errors, suitable for most situations). 
  if (eps==0,Eps_= 4.0*10.0^(-default(realprecision)),
             if (eps>0.0,Eps_=eps));
  if (imx==0,MaxIter_=10000,if (imx>0,MaxIter_=imx));
}

\\ ==========================================================
\\ Extensions of the floor(x) function
\\ ==========================================================

Floor(z) = {
/* ----------------------------------------------------------
Same as floor(x), but z may be also complex, in which case
it operates separately on both real and imaginary parts.
If the argument is anything else than t_REAL or t_COMPLEX
(such as t_INT), it is returned unmodified.
---------------------------------------------------------- */
  if (type(z)=="t_REAL",return(floor(z)));
  if (type(z)=="t_COMPLEX",
      return(floor(real(z))+I*floor(imag(z))));
  return(z);
}

Round(z) = {
/* ----------------------------------------------------------
Rounds to nearest integer. z may be complex, in which case
it operates separately on both real and imaginary parts.
If the argument is anything else than t_REAL or t_COMPLEX
(such as t_INT), it is returned unmodified.
---------------------------------------------------------- */
  if (type(z)=="t_REAL",return(floor(z+0.5)));
  if (type(z)=="t_COMPLEX",
      return(floor(real(z)+0.5)+I*floor(imag(z)+0.5)));
  return(z);
}

\\ ==========================================================
\\ Testing whether a t_REAL (or t_COMPLEX) matches an integer
\\ ==========================================================

IsInt(z) = {
/* ----------------------------------------------------------
Tests whether z is (practically) an integer.
Argument z may be of type INT, REAL, or COMPLEX.
For REAL and COMPLEX values, 'practically' means within Eps_
Note: when z is t_COMPLEX and an integer, imag(z) is zero
---------------------------------------------------------- */
  if (type(z)=="t_INT",return(1));
  my(x);
  if(real(imag(z))>Eps_,return(0),x=real(z));
  if(abs(x-Round(x))>Eps_,return(0),return(1));
}

IsIntLt0(z) = {
/* ----------------------------------------------------------
Tests whether z is (practically) a negative integer.
Argument z may be of type INT, REAL, or COMPLEX.
---------------------------------------------------------- */
  if (type(z)=="t_INT",return(z<0));
  my(x);
  if(abs(imag(z))>Eps_,return(0),x=real(z));
  if(x+1.0>Eps_,return(0),
     if(abs(x-Round(x))>Eps_,return(0),return(1)));
}

IsIntLe0(z) = {
/* ----------------------------------------------------------
Tests whether z is (practically) a non-positive integer.
Argument z may be of type INT, REAL, or COMPLEX.
---------------------------------------------------------- */
  if (type(z)=="t_INT",return(z<=0));
  my(x);
  if(abs(imag(z))>Eps_,return(0),x=real(z));
  if(x>Eps_,return(0),
     if(abs(x-Round(x))>Eps_,return(0),return(1)));
}

IsIntGt0(z) = {
/* ----------------------------------------------------------
Tests whether z is (practically) a positive integer.
Argument z may be of type INT, REAL, or COMPLEX.
---------------------------------------------------------- */
  if (type(z)=="t_INT",return(z>0));
  my(x);
  if(abs(imag(z))>Eps_,return(0),x=real(z));
  if(x-1.0<-Eps_,return(0),
     if(abs(x-Round(x))>Eps_,return(0),return(1)));
}

IsIntGe0(x) = {
/* ----------------------------------------------------------
Tests whether z is (practically) a non-negative integer.
Argument z may be of type t_INT, t_REAL, or t_COMPLEX.
---------------------------------------------------------- */
  if (type(z)=="t_INT",return(z>=0));
  my(x);
  if(abs(imag(z))>Eps_,return(0),x=real(z));
  if(x<-Eps_,return(0),
     if(abs(x-Round(x))>Eps_,return(0),return(1)));
}

IsEq(z1,z2) = {
/* ----------------------------------------------------------
Tests whether z1 is (practically) equal to z2.
The types of z1 and z2 may be t_INT, t_REAL, or t_COMPLEX.
They do not need to be of the same type.
---------------------------------------------------------- */
  if (type(z1)=="t_INT" & type(z2)=="t_INT",return(z1==z2));
  my(x = abs(z1+0.0-z2));
  if(abs(x)>Eps_,return(0),return(1));
}

/*
=============================================================
 Contributed to OEIS Wiki by Stanislav Sykora
=============================================================
*/
